;-------------------------------------------------------------------------------
format ELF64 executable 3
entry start

SDL_INIT_TIMER = 0x00000001
SDL_INIT_VIDEO = 0x00000020
SDL_WINDOW_OPENGL = 0x00000002
SDL_WINDOW_SHOWN = 0x00000004
SDL_WINDOW_BORDERLESS = 0x00000010
SDL_KEYDOWN = 0x300
SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000

GL_VERTEX_SHADER = 0x8B31
GL_FRAGMENT_SHADER = 0x8B30

WIN_W = 1280
WIN_H = 720

DEBUG = 1

macro emit [param] {param}
;-------------------------------------------------------------------------------
include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym
;-------------------------------------------------------------------------------
segment readable executable

start:
    mov rbp,rsp
    sub rsp,2048
    ; define local variables
    emit\
        <glCreateShader equ rbp-8>,<glShaderSource equ rbp-16>,\
        <glCompileShader equ rbp-24>,\
        <glAttachShader equ rbp-32>,\
        <glUniform1f equ rbp-40>,<glRecti equ rbp-48>,\
        <SDL_PollEvent equ rbp-56>,<SDL_GetTicks equ rbp-64>,\
        <SDL_GL_SwapWindow equ rbp-72>,\
        <time_loc equ rbp-80>,<sh equ rbp-88>,<prog equ rbp-96>,\
        <libsdl equ rbp-104>,<win equ rbp-112>,<event equ rbp-160>,\
        <libc equ rbp-512>,<printf equ rbp-520>,\
        <libbass equ rbp-528>,<stream equ rbp-532>,\
        <log equ rbp-2048>
if DEBUG = 1
    ; load libc
    emit <mov rdi,g_str_libc>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov [libc],rax>
    ; load printf
    emit <mov rdi,[libc]>,<mov rsi,g_str_printf>,<call [dlsym]>,<mov [printf],rax>
end if
    ; load libSDL-1.3.so.0
    emit <mov rdi,g_str_libsdl>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov [libsdl],rax>
    ; SDL_Init
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_Init>,<call [dlsym]>
    emit <mov edi,SDL_INIT_TIMER+SDL_INIT_VIDEO>,<call rax>
    ; create window
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_CreateWindow>,<call [dlsym]>
    emit <xor edi,edi>,<mov esi,SDL_WINDOWPOS_UNDEFINED>,<mov edx,SDL_WINDOWPOS_UNDEFINED>,<mov ecx,WIN_W>,<mov r8d,WIN_H>,\
        <mov r9d,SDL_WINDOW_SHOWN+SDL_WINDOW_OPENGL+SDL_WINDOW_BORDERLESS>,<call rax>,<mov [win],rax>
    ; create context
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_GL_CreateContext>,<call [dlsym]>
    emit <mov rdi,[win]>,<call rax>
    ; load needed SDL functions
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_PollEvent>,<call [dlsym]>,<mov [SDL_PollEvent],rax>
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_GL_SwapWindow>,<call [dlsym]>,<mov [SDL_GL_SwapWindow],rax>
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_GL_GetProcAddress>,<call [dlsym]>,<mov rbx,rax>
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_GetTicks>,<call [dlsym]>,<mov [SDL_GetTicks],rax>
    ; load needed GL commands
    emit <mov rdi,g_str_glCreateShader>,<call rbx>,<mov [glCreateShader],rax>
    emit <mov rdi,g_str_glShaderSource>,<call rbx>,<mov [glShaderSource],rax>
    emit <mov rdi,g_str_glCompileShader>,<call rbx>,<mov [glCompileShader],rax>
    emit <mov rdi,g_str_glAttachShader>,<call rbx>,<mov [glAttachShader],rax>
    emit <mov rdi,g_str_glUniform1f>,<call rbx>,<mov [glUniform1f],rax>
    emit <mov rdi,g_str_glRecti>,<call rbx>,<mov [glRecti],rax>
    ; create GLSL program
    emit <mov rdi,g_str_glCreateProgram>,<call rbx>,<call rax>,<mov [prog],eax>
    ; create and attach vertex shader
    emit <mov edi,GL_VERTEX_SHADER>,<call qword [glCreateShader]>,<mov [sh],eax>
    emit <mov edi,[prog]>,<mov esi,[sh]>,<call qword [glAttachShader]>
    emit <mov edi,[sh]>,<mov esi,2>,<mov rdx,g_src_vs_ptr>,<xor ecx,ecx>,<call qword [glShaderSource]>
    emit <mov edi,[sh]>,<call qword [glCompileShader]>
    ; create and attach fragment shader
    emit <mov edi,GL_FRAGMENT_SHADER>,<call qword [glCreateShader]>,<mov [sh],eax>
    emit <mov edi,[prog]>,<mov esi,[sh]>,<call qword [glAttachShader]>
    emit <mov edi,[sh]>,<mov esi,2>,<mov rdx,g_src_fs_ptr>,<xor ecx,ecx>,<call qword [glShaderSource]>
    emit <mov edi,[sh]>,<call qword [glCompileShader]>
    ; link program
    emit <mov rdi,g_str_glLinkProgram>,<call rbx>
    emit <mov edi,[prog]>,<call rax>
    ; use program
    emit <mov rdi,g_str_glUseProgram>,<call rbx>
    emit <mov edi,[prog]>,<call rax>
if DEBUG = 1
    ; print GLSL info log
    emit <mov rdi,g_str_glGetProgramInfoLog>,<call rbx>
    emit <mov edi,[prog]>,<mov esi,1024>,<xor edx,edx>,<lea rcx,[log]>,<call rax>
    emit <mov rdi,g_str_log>,<lea rsi,[log]>,<call qword [printf]>
end if
    ; get <t> uniform location
    emit <mov rdi,g_str_glGetUniformLocation>,<call rbx>
    emit <mov edi,[prog]>,<mov rsi,g_str_time>,<call rax>,<mov [time_loc],eax>

    ; load BASS library
    emit <mov rdi,g_str_libbass>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov [libbass],rax>
    ; BASS_Init
    emit <mov rdi,[libbass]>,<mov rsi,g_str_BASS_Init>,<call [dlsym]>
    emit <mov edi,-1>,<mov esi,44100>,<xor edx,edx>,<xor ecx,ecx>,<xor r8d,r8d>,<call rax>
    ; BASS_StreamCreateFile
    emit <mov rdi,[libbass]>,<mov rsi,g_str_BASS_StreamCreateFile>,<call [dlsym]>
    emit <xor edi,edi>,<mov rsi,g_str_intro>,<xor edx,edx>,<xor ecx,ecx>,<xor r8d,r8d>,<call rax>,<mov [stream],eax>
if DEBUG = 1
    emit <mov rdi,g_str_p>,<mov esi,eax>,<call qword [printf]>
end if
    ; BASS_ChannelPlay
    emit <mov rdi,[libbass]>,<mov rsi,g_str_BASS_ChannelPlay>,<call [dlsym]>
    emit <mov edi,[stream]>,<xor esi,esi>,<call rax>

      .main_loop:
    ; process window events
    emit <lea rdi,[event]>,<call qword [SDL_PollEvent]>
    cmp dword [event+0],SDL_KEYDOWN
    je .main_loop_end
    ; get time
    emit <call qword [SDL_GetTicks]>
    ; set <t> uniform
    emit <cvtsi2ss xmm0,eax>,<divss xmm0,[g_ticks_per_sec]>,<mov edi,[time_loc]>,<call qword [glUniform1f]>
    ; draw
    emit <mov edi,-1>,<mov esi,-1>,<mov edx,1>,<mov ecx,1>,<call qword [glRecti]>
    ; swap
    emit <mov rdi,[win]>,<call qword [SDL_GL_SwapWindow]>
    jmp .main_loop
      .main_loop_end:

    ; BASS_Free
    emit <mov rdi,[libbass]>,<mov rsi,g_str_BASS_Free>,<call [dlsym]>,<call rax>
    ; SDL_Quit
    emit <mov rdi,[libsdl]>,<mov rsi,g_str_SDL_Quit>,<call [dlsym]>,<call rax>
    ; terminate process
    emit <mov eax,60>,<xor edi,edi>,syscall
;-------------------------------------------------------------------------------
align 1
g_src_common:
db  '#version 400 compatibility',10,0

g_src_vs:
db  'void main(){'
db      'gl_Position=gl_Vertex;'
db  '}',0

g_src_fs:
db  'vec3 mod289(vec3 x){'
db      'return x-floor(x*(1./289.))*289.;'
db  '}'
db  'vec4 mod289(vec4 x){'
db      'return x-floor(x*(1./289.))*289.;'
db  '}'
db  'vec4 permute(vec4 x){'
db      'return mod289(((x*34.)+1.)*x);'
db  '}'
db  'vec4 taylorInvSqrt(vec4 r){'
db      'return 1.79284291400159-.85373472095314*r;'
db  '}'
db  'float snoise(vec3 v){'
db      'vec2 C=vec2(1./6.,1./3.);'
db      'vec4 D=vec4(0.,.5,1.,2.);'
db      'vec3 i=floor(v+dot(v,C.yyy));'
db      'vec3 x0=v-i+dot(i,C.xxx);'
db      'vec3 g=step(x0.yzx,x0.xyz);'
db      'vec3 l=1.-g;'
db      'vec3 i1=min(g.xyz,l.zxy);'
db      'vec3 i2=max(g.xyz,l.zxy);'
db      'vec3 x1=x0-i1+C.xxx;'
db      'vec3 x2=x0-i2+C.yyy;'
db      'vec3 x3=x0-D.yyy;'
db      'i=mod289(i);'
db      'vec4 p=permute(permute(permute(i.z+vec4(0.,i1.z,i2.z,1.))+i.y+vec4(0.,i1.y,i2.y,1.))+i.x+vec4(0.,i1.x,i2.x,1.));'
db      'float n_=.142857142857;'
db      'vec3 ns=n_*D.wyz-D.xzx;'
db      'vec4 j=p-49.0*floor(p*ns.z*ns.z);'
db      'vec4 x_=floor(j*ns.z);'
db      'vec4 y_=floor(j-7.*x_);'
db      'vec4 x=x_*ns.x+ns.yyyy;'
db      'vec4 y=y_*ns.x+ns.yyyy;'
db      'vec4 h=1.-abs(x)-abs(y);'
db      'vec4 b0=vec4(x.xy,y.xy);'
db      'vec4 b1=vec4(x.zw,y.zw);'
db      'vec4 s0=floor(b0)*2.+1.;'
db      'vec4 s1=floor(b1)*2.+1.;'
db      'vec4 sh=-step(h,vec4(0.));'
db      'vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy;'
db      'vec4 a1=b1.xzyw+s1.xzyw*sh.zzww;'
db      'vec3 p0=vec3(a0.xy,h.x);'
db      'vec3 p1=vec3(a0.zw,h.y);'
db      'vec3 p2=vec3(a1.xy,h.z);'
db      'vec3 p3=vec3(a1.zw,h.w);'
db      'vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3)));'
db      'p0*=norm.x;'
db      'p1*=norm.y;'
db      'p2*=norm.z;'
db      'p3*=norm.w;'
db      'vec4 m=max(.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.);'
db      'm=m*m;'
db      'return 42.*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));'
db  '}'
db  'uniform float t;'
db  'void main(){'
db      'vec2 p=gl_FragCoord.xy/vec2(1280,720);'
db      'gl_FragColor=vec4(.5+.5*snoise(vec3(4.*p,t)));'
db  '}',0

g_str_SDL_Init db 'SDL_Init',0
g_str_SDL_Quit db 'SDL_Quit',0
g_str_SDL_CreateWindow db 'SDL_CreateWindow',0
g_str_SDL_PollEvent db 'SDL_PollEvent',0
g_str_SDL_GetTicks db 'SDL_GetTicks',0
g_str_SDL_GL_CreateContext db 'SDL_GL_CreateContext',0
g_str_SDL_GL_SwapWindow db 'SDL_GL_SwapWindow',0
g_str_SDL_GL_GetProcAddress db 'SDL_GL_GetProcAddress',0
g_str_glCreateShader db 'glCreateShader',0
g_str_glShaderSource db 'glShaderSource',0
g_str_glCompileShader db 'glCompileShader',0
g_str_glCreateProgram db 'glCreateProgram',0
g_str_glLinkProgram db 'glLinkProgram',0
g_str_glUseProgram db 'glUseProgram',0
g_str_glAttachShader db 'glAttachShader',0
g_str_glGetUniformLocation db 'glGetUniformLocation',0
g_str_glUniform1f db 'glUniform1f',0
g_str_glRecti db 'glRecti',0
g_str_libsdl db 'libSDL-1.3.so.0',0
g_str_time db 't',0
g_str_libbass db './libbass.so',0
g_str_BASS_Init db 'BASS_Init',0
g_str_BASS_StreamCreateFile db 'BASS_StreamCreateFile',0
g_str_BASS_ChannelPlay db 'BASS_ChannelPlay',0
g_str_BASS_Free db 'BASS_Free',0
g_str_intro db 'intro.mp3',0
if DEBUG = 1
g_str_libc db 'libc.so.6',0
g_str_printf db 'printf',0
g_str_glGetProgramInfoLog db 'glGetProgramInfoLog',0
g_str_log db 'GLSL info log:',10,'%s',10,0
g_str_d db '%d',10,0
g_str_ld db '%ld',10,0
g_str_p db '%p',10,0
end if

align 4
g_ticks_per_sec dd 1000.0

align 8
g_src_vs_ptr dq g_src_common,g_src_vs
g_src_fs_ptr dq g_src_common,g_src_fs
;-------------------------------------------------------------------------------
